package eng.jTrussSolver.GUI;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.HashMap;
import javax.swing.JPanel;
import eng.geom.GeometricTransformations;
import eng.jTrussSolver.AssemblerResults.RodDisplacementData;
import eng.jTrussSolver.Force.ForceData;
import eng.jTrussSolver.Rod.RodData;
import eng.jTrussSolver.Solver.StiffnessMatrixAssembler;
import eng.jTrussSolver.Solver.truss2dProblemDef;


public class Truss2dPanelAfterAssembler extends JPanel {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private Ellipse2D.Double[] shNodes;
	private Ellipse2D.Double[] shDisplacementNodes;
	private ArrayList<Polygon> shRods;
	private ArrayList<Polygon> shDisplacementRods;
	private ArrayList<Polygon> shForces = new ArrayList<Polygon>();
    Graphics2D g2d;
	private truss2dProblemDef tr2d;
	private final int _RodWidth_pxl = 4;
	private final int _ForceWidth_pxl = 2;
	private final int _NodeSize_pxl = 8;
	private final int frameXpxl = 340;
	private final int frameYpxl = 340;
	

	/** constructor 
	 * 
	 */
	public Truss2dPanelAfterAssembler() {

		int nmax=10;
		shNodes = new Ellipse2D.Double[nmax];


		for(int i =0;i<nmax;i++)
		{
			double x,y;
			x=y=i*10.0;
			shNodes[i]=new Ellipse2D.Double(x, y,3,3);
		}

	}

	/** constructor 
	 * 
	 */
	public Truss2dPanelAfterAssembler(truss2dProblemDef trussProblem, StiffnessMatrixAssembler SMA) {
		updateProblemRepresentation(trussProblem,SMA);
	}

	
	public void updateProblemRepresentation(truss2dProblemDef problem, StiffnessMatrixAssembler SMA)
	{
		this.tr2d = problem;
		drawNodes();
		drawRods();
		drawForces();
		drawDisplacementNodes();
		drawDisplacementRods();
		
	}
	

	/***
	 * Drawing of nodes
	 */
	private void drawNodes()
	{
		int nmax=this.tr2d.getNodes().getNodesCount();
		shNodes = new Ellipse2D.Double[nmax];
		double findXmax, findXmin;
		double findYmin, findYmax;
		findXmin = tr2d.getNodes().get(nmax-1).X;
		findXmax = tr2d.getNodes().get(nmax-1).X;
		findYmax = tr2d.getNodes().get(nmax-1).Y;
		findYmin = tr2d.getNodes().get(nmax-1).Y;
		for(int i =0;i<nmax;i++)
		{// use this for to identify the data.
			if (tr2d.getNodes().get(i).X < findXmin ) {
				findXmin = tr2d.getNodes().get(i).X;
			}
			if (tr2d.getNodes().get(i).X > findXmax ) {
				findXmax = tr2d.getNodes().get(i).X;
			}
			if (tr2d.getNodes().get(i).Y < findYmin ) {
				findYmin = tr2d.getNodes().get(i).Y;
			}
			if (tr2d.getNodes().get(i).Y > findYmax ) {
				findYmax = tr2d.getNodes().get(i).Y;
			}
		}

		for(int i =0;i<nmax;i++)
		{
			double x,y;
			x=(tr2d.getNodes().get(i).X- findXmin)/(findXmax-findXmin)* frameXpxl +100;
			y= 450 - ((tr2d.getNodes().get(i).Y- findYmin)/(findYmax-findYmin)* frameYpxl + 10);
			shNodes[i]=new Ellipse2D.Double(x, y, _NodeSize_pxl, _NodeSize_pxl);

		}


	}

	public void paintComponent(Graphics g) {
		clear(g);
		g2d = (Graphics2D) g;

		for (int i=0;i<shRods.size();i++)
		{
			g2d.setColor(Color.BLACK);
			g2d.drawPolygon(shRods.get(i));

		}
		for (int i=0;i<shNodes.length;i++)
		{
			g2d.setColor(Color.GRAY);
			g2d.fill(shNodes[i]);
			g2d.draw(shNodes[i]);

		}
		for (int i=0; i<shForces.size(); i++) {
			g2d.setColor(Color.BLUE);
			g2d.drawPolygon(shForces.get(i));
		}
		for (int i=0; i<shDisplacementNodes.length;i++)
		{
			g2d.setColor(Color.GREEN);
			g2d.fill(shDisplacementNodes[i]);
			g2d.draw(shDisplacementNodes[i]);

		}
		for (int i=0; i<shDisplacementRods.size(); i++)
		{
			
			// http://docs.oracle.com/javase/tutorial/2d/geometry/strokeandfill.html
			float dash1[] = {10.0f};
			BasicStroke dashed =  new BasicStroke(1.0f,
                    BasicStroke.CAP_BUTT,
                    BasicStroke.JOIN_MITER,
                    10.0f, dash1, 0.0f);
			g2d.setColor(Color.RED);
			g2d.setStroke(dashed);
			g2d.drawPolygon(shDisplacementRods.get(i));

		}
	}
	
	/**
	 * Function responsible for drawing the Rod Polygons.
	 */
	private void drawRods()
	{
		shRods= new ArrayList<Polygon>();
		HashMap<Integer, RodData> rods=this.tr2d.getRods();
		System.out.println("Rods: " + rods.size());
		RodData b_tmp = null;

		for(int i=0; i<rods.size();i++)
		{
			b_tmp = rods.get(i);
			System.out.println(b_tmp.getRodDataAsString());
			shRods.add( calcRodPolygon(b_tmp));
		}
	}

	/**
	 * Draws forces.
	 */
	private void drawForces() {

		shForces.clear();
		HashMap<Integer, ForceData> forces = this.tr2d.getForces();
		System.out.println("\n" + "forces: " + forces.size());
		ForceData f_tmp = null;

		for(int i=0; i<forces.size();i++)
		{
			f_tmp = forces.get(i);
			System.out.println(f_tmp.getForceDataAsString());
			shForces.add( createBasicForcePolygon(f_tmp));
		}

	}

	/**
	 * Draw a basic Arrow pointing at 0 degrees, starting from 0,0
	 * then rotate it and 
	 * finally translate it.
	 * @param f_tmp
	 * @return
	 */
	private Polygon createBasicForcePolygon(ForceData f_tmp) {

		Polygon shForcePolygon = basicForcePolygon();

		//rotate each point.
		double angle = f_tmp.getAngle_Rad();
		Point tmpPoint = new Point();
		for(int i=0; i< shForcePolygon.npoints; i++)
		{

			tmpPoint = GeometricTransformations.rotatePoint(
					new Point(shForcePolygon.xpoints[i], shForcePolygon.ypoints[i]),
					//TODO: center point fixed
					new Point(8,4), 
					angle);
			shForcePolygon.xpoints[i] = tmpPoint.x;
			shForcePolygon.ypoints[i] = tmpPoint.y;
			
		}
		// translate each point.
		shForcePolygon.translate((int)((shNodes[f_tmp.getNodeIDofForce()].x)), (int)((shNodes[f_tmp.getNodeIDofForce()].y)));
		return shForcePolygon;
	}

	/**
	 * Generates an horizontal arrow, which is used for force representation
	 * @return
	 */
	public Polygon basicForcePolygon() {
		Polygon shForcePolygon = new Polygon();
		int x1=0, x2=0, x3=0, x4=0, x5=0, x6=0, x7=0,
				y1=0, y2=0, y3=0, y4=0, y5=0, y6=0, y7=0;
		int xstart;
		int ystart;

		xstart = + this._NodeSize_pxl;
		ystart =+ this._NodeSize_pxl/2;

		x1 = xstart;
		y1 = ystart - this._ForceWidth_pxl;

		x2 = xstart;
		y2 = ystart  + this._ForceWidth_pxl;

		x3 = x2 + 50;
		y3 = y2;

		x4 = x3;
		y4 = y3 + (2*this._ForceWidth_pxl);

		x5 = xstart + 50 + 10;
		y5 = ystart;

		x6 = x4;
		y6 = y3 - 4*this._ForceWidth_pxl;

		x7 = x4;
		y7 = y1;
		
		shForcePolygon.addPoint(x1, y1);
		shForcePolygon.addPoint(x2, y2);
		shForcePolygon.addPoint(x3, y3);
		shForcePolygon.addPoint(x4, y4);
		shForcePolygon.addPoint(x5, y5);
		shForcePolygon.addPoint(x6, y6);
		shForcePolygon.addPoint(x7, y7);
		return shForcePolygon;
	}


	/**
	 * Calculates Rod polygon
	 * @param b_tmp
	 * @param RodWidthInPxl : Rod width in Pixels
	 */
	private Polygon calcRodPolygon(RodData b_tmp) {

		Polygon shRodPolygon=new Polygon();
		int x1, x2, x3, x4, y1, y2, y3, y4;

		//TODo change from shape Nodes. this is unsafe!!!!
		int xstart = (int)((shNodes[b_tmp.NodeStart].x));
		int ystart = (int)((shNodes[b_tmp.NodeStart].y));
		int xend = (int)((shNodes[b_tmp.NodeEnd].x));
		int yend = (int)((shNodes[b_tmp.NodeEnd].y));

		double rodLength = Math.sqrt(Math.pow(ystart-yend,2)+ Math.pow(xstart-xend,2));
		double angle = GeometricTransformations.angleFull(xstart, ystart, xend, yend, rodLength);
		System.out.print("angle[Deg] = " + angle*180/Math.PI + "\n");
		x1= (int)(_NodeSize_pxl/2);
		y1=-(int)(_RodWidth_pxl/2);
		
		x2= (int)(_NodeSize_pxl/2);
		y2= (int)(_RodWidth_pxl/2); 
		
		x3= (int)(rodLength- _NodeSize_pxl/2);
		y3= (int)(_RodWidth_pxl/2);
		
		x4= (int)(rodLength - _NodeSize_pxl/2);
		y4=-(int)(_RodWidth_pxl/2);

		shRodPolygon.addPoint(x1, y1);
		shRodPolygon.addPoint(x2, y2);
		shRodPolygon.addPoint(x3, y3);
		shRodPolygon.addPoint(x4, y4);

		// rotate
		Point tmpPoint = new Point();
		for(int i=0; i< shRodPolygon.npoints; i++)
		{

			tmpPoint = GeometricTransformations.rotatePoint(
					new Point(shRodPolygon.xpoints[i], shRodPolygon.ypoints[i]),
					new Point(0,0), 
					angle);
			shRodPolygon.xpoints[i] = tmpPoint.x;
			shRodPolygon.ypoints[i] = tmpPoint.y;
			
		}
		// translate each point.
		shRodPolygon.translate(xstart + this._NodeSize_pxl/2, ystart+ this._NodeSize_pxl/2);

		//translate
		return shRodPolygon;
	}
	
	private Polygon calcRodDisplacementPolygon(RodDisplacementData DispRod_tmp) {

		Polygon shRodPolygon = new Polygon();
		int x1, x2, x3, x4, y1, y2, y3, y4;

		//TODo change from shape Nodes. this is unsafe!!!!
		int xstart = (int)((shDisplacementNodes[DispRod_tmp.NodeStart].x));
		int ystart = (int)((shDisplacementNodes[DispRod_tmp.NodeStart].y));
		int xend = (int)((shDisplacementNodes[DispRod_tmp.NodeEnd].x));
		int yend = (int)((shDisplacementNodes[DispRod_tmp.NodeEnd].y));

		double rodLength = Math.sqrt(Math.pow(ystart-yend,2)+ Math.pow(xstart-xend,2));
		double angle = GeometricTransformations.angleFull(xstart, ystart, xend, yend, rodLength);
		System.out.print("angle[Deg] = " + angle*180/Math.PI + "\n");
		x1= (int)(_NodeSize_pxl/2);
		y1=-(int)(_RodWidth_pxl/2);
		
		x2= (int)(_NodeSize_pxl/2);
		y2= (int)(_RodWidth_pxl/2); 
		
		x3= (int)(rodLength- _NodeSize_pxl/2);
		y3= (int)(_RodWidth_pxl/2);
		
		x4= (int)(rodLength - _NodeSize_pxl/2);
		y4=-(int)(_RodWidth_pxl/2);

		shRodPolygon.addPoint(x1, y1);
		shRodPolygon.addPoint(x2, y2);
		shRodPolygon.addPoint(x3, y3);
		shRodPolygon.addPoint(x4, y4);

		// rotate
		Point tmpPoint = new Point();
		for(int i=0; i< shRodPolygon.npoints; i++)
		{

			tmpPoint = GeometricTransformations.rotatePoint(
					new Point(shRodPolygon.xpoints[i], shRodPolygon.ypoints[i]),
					new Point(0,0), 
					angle);
			shRodPolygon.xpoints[i] = tmpPoint.x;
			shRodPolygon.ypoints[i] = tmpPoint.y;
			
		}
		// translate each point.
		shRodPolygon.translate(xstart + this._NodeSize_pxl/2, ystart+ this._NodeSize_pxl/2);

		//translate
		return shRodPolygon;
	}

	private void drawDisplacementNodes() {

		
		int nmax = this.tr2d.getNodeDisplacements().getNodeDisplacementCount();
		shDisplacementNodes = new Ellipse2D.Double[nmax];
		double findXmax, findXmin;
		double findYmin, findYmax;
		findXmin = tr2d.getNodeDisplacements().get(nmax-1).newX;
		findXmax = tr2d.getNodeDisplacements().get(nmax-1).newY;
		findYmax = tr2d.getNodeDisplacements().get(nmax-1).newY;
		findYmin = tr2d.getNodeDisplacements().get(nmax-1).newY;
		for(int i =0;i<nmax;i++)
		{// use this for to identify the data.
			if (tr2d.getNodeDisplacements().get(i).newX < findXmin ) {
				findXmin = tr2d.getNodeDisplacements().get(i).newX;
			}
			if (tr2d.getNodeDisplacements().get(i).newX > findXmax ) {
				findXmax = tr2d.getNodeDisplacements().get(i).newX;
			}
			if (tr2d.getNodeDisplacements().get(i).newY < findYmin ) {
				findYmin = tr2d.getNodeDisplacements().get(i).newY;
			}
			if (tr2d.getNodeDisplacements().get(i).newY > findYmax ) {
				findYmax = tr2d.getNodeDisplacements().get(i).newY;
			}
		}

		for(int i =0; i<nmax;i++)
		
		{
			double x,y;
			if (tr2d.getNodes().get(i).X != tr2d.getNodeDisplacements().get(i).newX) {
				x = ((tr2d.getNodeDisplacements().get(i).newX- findXmin)/(findXmax-findXmin)* frameXpxl) + 100 + (findXmax-findXmin)*0.2;
			}
			else {
				x = shNodes[i].x; 
			}
			
			if (tr2d.getNodes().get(i).Y != tr2d.getNodeDisplacements().get(i).newY) {
				y = 450 - ((tr2d.getNodeDisplacements().get(i).newY- findYmin)/(findYmax-findYmin)* frameYpxl) + 10 + (findYmax-findYmin)*0.2;
			}
			else {
				y = shNodes[i].y;
			}

			shDisplacementNodes[i] = new Ellipse2D.Double(x, y, _NodeSize_pxl, _NodeSize_pxl);

		}
		
	}
	
	private void drawDisplacementRods() {
		
		shDisplacementRods = new ArrayList<Polygon>();
		HashMap<Integer, RodDisplacementData> RodsDisplacement = this.tr2d.getRodDisplacements();
		System.out.println("Displacements Rods: " + RodsDisplacement.size());
		RodDisplacementData DispRod_tmp = null;

		for(int i=0; i<RodsDisplacement.size();i++)
		{
			DispRod_tmp = RodsDisplacement.get(i);
			System.out.println("\n" + DispRod_tmp.getRodDisplacementAsString());
			shDisplacementRods.add(calcRodDisplacementPolygon(DispRod_tmp));
		}
	}
	
	protected void clear(Graphics g) {
		super.paintComponent(g);
	}

	protected Ellipse2D.Double getCircle(int index) {
		return (shNodes[index]);
	}


}
